题面:http://www.51nod.com/Challenge/Problem.html#!#problemId=2383
输入一个长度为n的数组a[i],下标从0开始(0到n-1)
保证n是2的整数次幂,
对于每个i (0 <= i < n)
求所有满足((i & j) == j)的a[j]之和。
其中&表示按位与,即C++和C中的&,Pascal中的and。
对于100%的数据,1 <= n <= 220, 0 <= a[i] <= 1000
对于70%的数据,1 <= n <= 215,
对于50%的数据,1 <= n <= 210,
考试的时候居然忽略了提示QWQ!!!
虽然这是一个简单题,但是为了降低难度,你可以看看下面的解释。
对于一个一维数组求部分和,可以使用如下代码
1 | for (int i = 1; i <= n; i++)a[i] += a[i - 1]; |
对于一个二维数组求部分和,可以使用如下代码
1 | for (int i = 1; i <= n; i++) |
或如下代码
1 | for (int i = 1; i <= n; i++) |
第二份代码看起来更麻烦更慢,来考虑一下三维的情况。
1 | for (int i = 1; i <= n; i++) |
或如下代码
1 | for (int i = 1; i <= n; i++) |
第二份代码就不一定更慢了(第二份复杂度大约3n^3,第一份复杂度大概8n^3)
随着维度更高,第一份代码容斥时项数越来越多,而第二份只是多一次遍历整个数组,优势越来越大。
同样的思路能不能推广到更高维的情况呢?
输入
1 | 第一行一个整数n |
输出
1 | 输出共n行,其中第i(0 <= i < n)行表示i的答案。 |
输入样例
1 | 8 |
输出样例
1 | 1 |
首先,这题一眼容斥,又或者是FWT/FMT?(没差,都不会)
还记得有两个**学了一上午FMT/FMT觉得自己会了的心酸历程
但是一看题目说是简单题?!
让我们来看看提示(!一定要看啊)
写的是 容斥 与 从低位到高位一维一维做前缀和 这两种方法的区别
!!一维一维做前缀和???!!!
当二维时举个例子
显然由题意可得当 (i&j==j) 时 j是i的子集
所以:
$f[0]=a[0]$
$f[1]=a[1]+a[0]$
$f[2]=a[2]+a[0]$
$f[3]=a[3]+a[2]+a[1]+a[0]$
然后我们让他更显然:
$f[0][0]=a[0]$
$f[0][1]=a[1]+a[0]$
$f[1][0]=a[2]+a[0]$
$f[1][1]=a[3]+a[2]+a[1]+a[0]$
然后根据提示:先转移最后一维
- $f$初值为输入值
$f[0][1]+=f[0][0]$
$f[1][1]+=f[1][0]$
第二维:
$f[1][0]+=f[0][0]$
$f[1][1]+=f[0][1]$
而这道题就是一个21维的$f$数组,如果有铁头娃想要21重循环,写21次的话,也不是不行
当然我选择状压(害怕
1 |
|
总的来说,这题还是很遗憾,没有看提示(看了提示也不一定能写出来啊喂)
注意io优化,看清题目!